import { expect, it, describe } from 'vitest';
import gql from 'graphql-tag';
import { queryAsAdmin, TEST_ORGANIZATION, USER_PARTICIPATE } from '../../utils/testQuery';
import { queryAsAdminWithSuccess } from '../../utils/testQueryHelper';

const LIST_QUERY = gql`
  query malwares(
    $first: Int
    $after: ID
    $orderBy: MalwaresOrdering
    $orderMode: OrderingMode
    $filters: FilterGroup
    $search: String
  ) {
    malwares(
      first: $first
      after: $after
      orderBy: $orderBy
      orderMode: $orderMode
      filters: $filters
      search: $search
    ) {
      edges {
        node {
          id
          name
          description
        }
      }
    }
  }
`;

const READ_QUERY = gql`
  query malware($id: String!) {
    malware(id: $id) {
      id
      standard_id
      name
      description
      killChainPhases {
        id
        standard_id
      }
      toStix
    }
  }
`;

describe('Malware resolver standard behavior', () => {
  let malwareInternalId: string;
  const malwareStixId = 'malware--34c9875d-8206-4f4b-bf17-f58d9cf7ebec';
  it('should malware created', async () => {
    const CREATE_QUERY = gql`
      mutation MalwareAdd($input: MalwareAddInput!) {
        malwareAdd(input: $input) {
          id
          name
          description
        }
      }
    `;
    // Create the malware
    const MALWARE_TO_CREATE = {
      input: {
        name: 'Malware',
        stix_id: malwareStixId,
        description: 'Malware description',
        killChainPhases: ['kill-chain-phase--56330302-292c-5ad4-bece-bacaa99c16e0'],
      },
    };
    const malware = await queryAsAdmin({
      query: CREATE_QUERY,
      variables: MALWARE_TO_CREATE,
    });
    expect(malware).not.toBeNull();
    expect(malware.data?.malwareAdd).not.toBeNull();
    expect(malware.data?.malwareAdd.name).toEqual('Malware');
    malwareInternalId = malware.data?.malwareAdd.id;
  });
  it('should malware loaded by internal id', async () => {
    const queryResult = await queryAsAdmin({ query: READ_QUERY, variables: { id: malwareInternalId } });
    expect(queryResult).not.toBeNull();
    expect(queryResult.data?.malware).not.toBeNull();
    expect(queryResult.data?.malware.id).toEqual(malwareInternalId);
    expect(queryResult.data?.malware.toStix.length).toBeGreaterThan(5);
    expect(queryResult.data?.malware.killChainPhases.length).toEqual(1);
    expect(queryResult.data?.malware.killChainPhases[0].standard_id).toEqual(
      'kill-chain-phase--56330302-292c-5ad4-bece-bacaa99c16e0'
    );
  });
  it('should malware loaded by stix id', async () => {
    const queryResult = await queryAsAdmin({ query: READ_QUERY, variables: { id: malwareStixId } });
    expect(queryResult).not.toBeNull();
    expect(queryResult.data?.malware).not.toBeNull();
    expect(queryResult.data?.malware.id).toEqual(malwareInternalId);
  });
  it('should list malwares', async () => {
    const queryResult = await queryAsAdmin({ query: LIST_QUERY, variables: { first: 10 } });
    expect(queryResult.data?.malwares.edges.length).toEqual(3);
  });
  it('should update malware', async () => {
    const UPDATE_QUERY = gql`
      mutation MalwareEdit($id: ID!, $input: [EditInput]!) {
        malwareEdit(id: $id) {
          fieldPatch(input: $input) {
            id
            name
          }
        }
      }
    `;
    const queryResult = await queryAsAdmin({
      query: UPDATE_QUERY,
      variables: { id: malwareInternalId, input: { key: 'name', value: ['Malware - test'] } },
    });
    expect(queryResult.data?.malwareEdit.fieldPatch.name).toEqual('Malware - test');
  });
  it('should context patch malware', async () => {
    const CONTEXT_PATCH_QUERY = gql`
      mutation MalwareEdit($id: ID!, $input: EditContext) {
        malwareEdit(id: $id) {
          contextPatch(input: $input) {
            id
          }
        }
      }
    `;
    const queryResult = await queryAsAdmin({
      query: CONTEXT_PATCH_QUERY,
      variables: { id: malwareInternalId, input: { focusOn: 'description' } },
    });
    expect(queryResult.data?.malwareEdit.contextPatch.id).toEqual(malwareInternalId);
  });
  it('should context clean malware', async () => {
    const CONTEXT_PATCH_QUERY = gql`
      mutation MalwareEdit($id: ID!) {
        malwareEdit(id: $id) {
          contextClean {
            id
          }
        }
      }
    `;
    const queryResult = await queryAsAdmin({
      query: CONTEXT_PATCH_QUERY,
      variables: { id: malwareInternalId },
    });
    expect(queryResult.data?.malwareEdit.contextClean.id).toEqual(malwareInternalId);
  });
  it('should add relation in malware', async () => {
    const RELATION_ADD_QUERY = gql`
      mutation MalwareEdit($id: ID!, $input: StixRefRelationshipAddInput!) {
        malwareEdit(id: $id) {
          relationAdd(input: $input) {
            id
            from {
              ... on Malware {
                objectMarking {
                  id
                }
              }
            }
          }
        }
      }
    `;
    const queryResult = await queryAsAdmin({
      query: RELATION_ADD_QUERY,
      variables: {
        id: malwareInternalId,
        input: {
          toId: 'marking-definition--78ca4366-f5b8-4764-83f7-34ce38198e27',
          relationship_type: 'object-marking',
        },
      },
    });
    expect(queryResult.data?.malwareEdit.relationAdd.from.objectMarking.length).toEqual(1);
  });
  it('should delete relation in malware', async () => {
    const RELATION_DELETE_QUERY = gql`
      mutation MalwareEdit($id: ID!, $toId: StixRef!, $relationship_type: String!) {
        malwareEdit(id: $id) {
          relationDelete(toId: $toId, relationship_type: $relationship_type) {
            id
            objectMarking {
              id
            }
          }
        }
      }
    `;
    const queryResult = await queryAsAdmin({
      query: RELATION_DELETE_QUERY,
      variables: {
        id: malwareInternalId,
        toId: 'marking-definition--78ca4366-f5b8-4764-83f7-34ce38198e27',
        relationship_type: 'object-marking',
      },
    });
    expect(queryResult.data?.malwareEdit.relationDelete.objectMarking.length).toEqual(0);
  });
  it('should malware deleted', async () => {
    const DELETE_QUERY = gql`
      mutation malwareDelete($id: ID!) {
        malwareEdit(id: $id) {
          delete
        }
      }
    `;
    // Delete the malware
    await queryAsAdmin({
      query: DELETE_QUERY,
      variables: { id: malwareInternalId },
    });
    // Verify is no longer found
    const queryResult = await queryAsAdmin({ query: READ_QUERY, variables: { id: malwareStixId } });
    expect(queryResult).not.toBeNull();
    expect(queryResult.data?.malware).toBeNull();
  });
});

describe('Malware createdBy relation verifications', () => {
  let malwareWithoutCreatedById: string;
  const malwareWithoutCreatedByIdName = 'Malware that is created without relation createdBy';
  const malwareIdForCleanup: string[] = [];

  it('should be forbidden to use an existing id (ex: User) instead of Identity id in createdBy', async () => {
    const CREATE_QUERY = gql`
      mutation MalwareAdd($input: MalwareAddInput!) {
        malwareAdd(input: $input) {
          name
          createdBy {
            id
          }
        }
      }
    `;
    const MALWARE_WITH_BAD_CREATED_BY = {
      input: {
        name: 'Malware should not be created with a userid as CreatedBy',
        createdBy: USER_PARTICIPATE.id,
      },
    };
    const malwareCreationResponse = await queryAsAdmin({
      query: CREATE_QUERY,
      variables: MALWARE_WITH_BAD_CREATED_BY,
    });
    expect(malwareCreationResponse.errors?.length, 'Error is expected when createdBy is not Identity.').toBe(1);
    expect(malwareCreationResponse.data?.malwareAdd, 'No data is expected when createdBy is not Identity.').toBeNull();
  });

  it('should be allowed to create a Malware with unknown createdBy', async () => {
    const CREATE_QUERY = gql`
      mutation MalwareAdd($input: MalwareAddInput!) {
        malwareAdd(input: $input) {
          id
          name
          createdBy {
            id
          }
        }
      }
    `;
    const MALWARE_WITH_NOT_EXISTING_CREATED_BY = {
      input: {
        name: 'Malware should be created without relation createdBy',
        createdBy: 'this-is-an-invalid-id',
      },
    };
    const malwareCreationResponse = await queryAsAdminWithSuccess({
      query: CREATE_QUERY,
      variables: MALWARE_WITH_NOT_EXISTING_CREATED_BY,
    });
    expect(malwareCreationResponse.data?.malwareAdd.createdBy).toBeNull();
    expect(malwareCreationResponse.data?.malwareAdd.id).toBeDefined();
    malwareIdForCleanup.push(malwareCreationResponse.data?.malwareAdd.id);
  });

  it('should be allowed to create a Malware with legit Identity in createdBy', async () => {
    const CREATE_QUERY = gql`
      mutation MalwareAdd($input: MalwareAddInput!) {
        malwareAdd(input: $input) {
          id
          name
          createdBy {
            id
          }
        }
      }
    `;
    const MALWARE_WITH_VALID_CREATED_BY = {
      input: {
        name: 'Malware should be created with createdBy',
        createdBy: TEST_ORGANIZATION.id,
      },
    };
    const malwareCreationResponse = await queryAsAdminWithSuccess({
      query: CREATE_QUERY,
      variables: MALWARE_WITH_VALID_CREATED_BY,
    });
    expect(malwareCreationResponse.data?.malwareAdd.createdBy).toBeDefined();
    expect(malwareCreationResponse.data?.malwareAdd.id).toBeDefined();
    malwareIdForCleanup.push(malwareCreationResponse.data?.malwareAdd.id);
  });

  it('should be forbidden to field patch with a CreatedBy that is not Identity', async () => {
    const CREATE_QUERY = gql`
      mutation MalwareAdd($input: MalwareAddInput!) {
        malwareAdd(input: $input) {
          name
          id
        }
      }
    `;
    const MALWARE_WITHOUT_CREATED_BY = {
      input: {
        name: malwareWithoutCreatedByIdName,
      },
    };

    const malwareCreationResponse = await queryAsAdminWithSuccess({
      query: CREATE_QUERY,
      variables: MALWARE_WITHOUT_CREATED_BY,
    });
    expect(malwareCreationResponse.data?.malwareAdd.id).toBeDefined();
    malwareWithoutCreatedById = malwareCreationResponse.data?.malwareAdd.id;
    malwareIdForCleanup.push(malwareWithoutCreatedById);

    const FIELD_PATCH_QUERY = gql`
      mutation MalwareEdit($id: ID!, $input: [EditInput]!) {
        malwareEdit(id: $id) {
          fieldPatch(input: $input) {
            id
            name
          }
        }
      }
    `;
    const queryFieldEditResult = await queryAsAdmin({
      query: FIELD_PATCH_QUERY,
      variables: { id: malwareWithoutCreatedById, input: { key: 'createdBy', value: USER_PARTICIPATE.id } },
    });
    expect(queryFieldEditResult.errors?.length, 'Error is expected when createdBy is not Identity.').toBe(1);
    expect(queryFieldEditResult.data?.malwareAdd, 'No data is expected when createdBy is not Identity.').toBeUndefined();
  });

  it('should not update CreatedBy that is not Identity on Upsert', async () => {
    const CREATE_QUERY = gql`
      mutation MalwareAdd($input: MalwareAddInput!) {
        malwareAdd(input: $input) {
          name
          createdBy {
            id
          }
        }
      }
    `;
    const MALWARE_WITH_BAD_CREATED_BY = {
      input: {
        name: malwareWithoutCreatedByIdName,
        createdBy: USER_PARTICIPATE.id,
      },
    };
    const malwareCreationResponse = await queryAsAdmin({
      query: CREATE_QUERY,
      variables: MALWARE_WITH_BAD_CREATED_BY,
    });
    expect(malwareCreationResponse.errors?.length, 'Error is expected when createdBy is not Identity.').toBe(1);
    expect(malwareCreationResponse.data?.malwareAdd, 'Error is expected on upsert.').toBeNull();
  });

  it('should be forbidden to relationAdd with a CreatedBy that is not Identity', async () => {
    const RELATION_ADD_QUERY = gql`
      mutation MalwareEdit($id: ID!, $input: StixRefRelationshipAddInput!) {
        malwareEdit(id: $id) {
          relationAdd(input: $input) {
            id
            relationship_type
          }
        }
      }
    `;
    const queryRelationAddResult = await queryAsAdmin({
      query: RELATION_ADD_QUERY,
      variables: { id: malwareWithoutCreatedById, input: { relationship_type: 'created-by', toId: USER_PARTICIPATE.id } },
    });
    expect(queryRelationAddResult.errors?.length, 'Error is expected when createdBy is not Identity.').toBe(1);
    expect(queryRelationAddResult.data?.malwareAdd, 'No data is expected when createdBy is not Identity.').toBeUndefined();
  });

  it('should malware used for tests deleted', async () => {
    const deleteMalwarebyId = async (malwareId: string) => {
      const DELETE_QUERY = gql`
        mutation malwareDelete($id: ID!) {
          malwareEdit(id: $id) {
            delete
          }
        }
      `;

      await queryAsAdmin({
        query: DELETE_QUERY,
        variables: { id: malwareId },
      });
    };
    for (let i = 0; i < malwareIdForCleanup.length; i += 1) {
      await deleteMalwarebyId(malwareIdForCleanup[i]);
    }
  });
});
